Sandbox.Denial: clean description, don't leak host paths#4
Merged
Conversation
`Sandbox.Denial` is thrown by `Shell.authorize(_:)` when policy
rejects a URL. The struct carries `url`, `reason`, and an
implementer-defined `suggestion: URL?` hint — the suggestion
typically encodes "where this URL would have landed under the
first sandbox root", which means it embeds the embedder's host
sandbox root.
For an iOS-app-as-sandbox embedder (Cocoanetics/iBash) that's a
direct PII-shaped leak. ArgumentParser's `fullMessage(for:)`
falls through to `String(describing: error)` for unrecognised
error types, and Swift's default reflective dump for a plain
struct prints every stored property:
Error: Denial(
url: file:///Users/.../Containers/.../Documents/Foo.bar,
reason: "file URL is outside sandbox root",
suggestion: Optional(file:///Users/.../home))
A SwiftPorts CLI like `gh auth login`, registered as a builtin
in an in-process bash, would print that to the user's terminal
on a single denied call.
Add `CustomStringConvertible` + `LocalizedError` conformances
that return only `reason`. Same surface for `"\(denial)"`,
`String(describing: denial)`, and `denial.localizedDescription`.
Callers that want the URLs read `.url` and `.suggestion`
directly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Sandbox.Denialis the errorShell.authorize(_:)throws when policy rejects a URL. It carries an implementer-definedsuggestion: URL?hint that, in practice, embeds the embedder's host sandbox root (the factory builds it ashintRoot.appendingPathComponent(...)inSandbox+Factories.swift).fullMessage(for:)falls through toString(describing: error)for unrecognised error types. For a plain struct that's a reflective dump of every stored property — including the host-path-bearing suggestion. Embedders running SwiftPorts CLIs (gh, glab, git, …) as in-process bash builtins (Cocoanetics/iBash uses this exact shape) end up printing the iOS app container path to the user's terminal on a single denied call:CustomStringConvertible+LocalizedErrorconformances that surface onlyreason. Callers that genuinely want the URLs still read.urland.suggestiondirectly.Test plan
swift test(33/33 passing)denialDescriptionDoesNotLeakUrlsregression — build aDenialwhoseurl/suggestionboth point at/secret/host/root/...and assert that"\(denial)",String(describing: denial), anddenial.localizedDescriptionall contain the reason but neither the host path, the literalsuggestionfield name, nor theDenial(struct shape.🤖 Generated with Claude Code